﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OCRTheNewHope
{
    public static class Extensions
    {
        public static double ToDegree(this double radian)
        {
            return (radian * 360) / (2 * Math.PI);
        }

        public static double ToRadian(this double degree)
        {
            return (degree * 2 * Math.PI) / (360);
        }

        public static double[,] Inverse2DMatrix(this double[,] matrix)
        {
            double[,] rVal = new double[2, 2];

            double p = 1.0 / (matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0]);


            rVal[0, 0] = p * matrix[1, 1];
            rVal[0, 1] = p * -1 * matrix[0, 1];
            rVal[1, 0] = p * -1 * matrix[1, 0];
            rVal[1, 1] = p * matrix[0, 0];

            return rVal;
        }

        public static double[, ,] GenerateTransformationMatrices(this double[,] columnVector)
        {
            double[, ,] returnValue = new double[12, 2, 1];
            int count = 0;
            for (int i = -7; i <= 7; i += 7)
            {
                for (int j = -15; j <= 15; j += 30)
                {
                    for (int k = 0; k < 2; k++)
                    {
                        double[,] affineMatrix = new double[2, 2];
                        if (k == 0)
                            affineMatrix = new double[2, 2] { { 0.95, 0 }, { 0, 1.05 } };
                        else
                            affineMatrix = new double[2, 2] { { 1.05, 0 }, { 0, 0.95 } };

                        returnValue[count, 0, 0] = columnVector.TransformationMatrix2D(j, i, affineMatrix)[0, 0];
                        returnValue[count, 1, 0] = columnVector.TransformationMatrix2D(j, i, affineMatrix)[1, 0];
                        count++;
                    }
                }

            }
            return returnValue;
        }

        public static double[,] TransformationMatrix2D(this double[,] columnVector, double affineDegree = 60, double rotationDegree = 8, double[,] affineMatrix = null)
        {
            columnVector = Synthesizer.Generate2DRotationMatrix(affineDegree).Multiply(columnVector);

            double[,] transformedMatrix = columnVector.AffineMatrix2D(affineMatrix);

            transformedMatrix = Synthesizer.Generate2DRotationMatrix(affineDegree).Inverse2DMatrix().Multiply(transformedMatrix);

            transformedMatrix = Synthesizer.Generate2DRotationMatrix(rotationDegree).Multiply(transformedMatrix);

            return transformedMatrix;
        }

        public static double[,] AffineMatrix2D(this double[,] columnVector, double[,] affineMatrix = null)
        {
            if (affineMatrix == null)
                affineMatrix = new double[2, 2] { { 0.8, 0 }, { 0, 1.2 } };

            double[,] resultingColumnVector = new double[2, 1];

            resultingColumnVector = affineMatrix.Multiply(columnVector);

            return resultingColumnVector;
        }

        public static double[,] Multiply(this double[,] matrice1, double[,] matrice2)
        {
            double[,] resultingMatrice = new double[matrice1.GetLength(0), matrice2.GetLength(1)];

            if (matrice1.GetLength(1) == matrice2.GetLength(0))
            {
                for (int i = 0; i < matrice1.GetLength(0); i++)
                {
                    for (int j = 0; j < matrice2.GetLength(1); j++)
                    {
                        for (int k = 0; k < matrice1.GetLength(1); k++)
                        {
                            resultingMatrice[i, j] += matrice1[i, k] * matrice2[k, j];
                        }
                    }
                }
            }

            return resultingMatrice;
        }

        public static double[] Multiply(this double[] vector, double[,] matrix)
        {
            double[] rVal = new double[2];

            if (vector.Length != matrix.GetLength(0))
                throw new Exception("Matrix dimensions must agree");

            for (int i = 0; i < rVal.Length; i++)
            {
                double sum = 0;
                for (int j = 0; j < rVal.Length; j++)
                {
                    sum += vector[j] * matrix[j, i];
                }
                rVal[i] = sum;
            }

            return rVal;
        }

        public static int MaxIndex(this double[] sequence)
        {
            int maxIndex = 0;
            double maxValue = sequence[0];

            for (int i = 1; i < sequence.Length; i++)
            {
                if (sequence[i] > maxValue)
                {
                    maxValue = sequence[i];
                    maxIndex = i;
                }
            }

            return maxIndex;
        }

        public static int MaxIndex(this int[] sequence)
        {
            int maxIndex = 0;
            int maxValue = sequence[0];

            for (int i = 1; i < sequence.Length; i++)
            {
                if (sequence[i] > maxValue)
                {
                    maxValue = sequence[i];
                    maxIndex = i;
                }
            }

            return maxIndex;
        }

        public static void Add(this double[] histogram, double[] temp)
        {
            for (int i = 0; i < histogram.Length; i++)
            {
                histogram[i] += temp[i];
            }
        }

        public static void Multiply(this double[] histogram, double weight)
        {
            for (int i = 0; i < histogram.Length; i++)
            {
                histogram[i] = histogram[i] * weight;
            }
        }
    }
}
